/**************************************************************************
 *                                                                        *
 *         Copyright (c) 2014 by Generalplus Inc.                         *
 *                                                                        *
 *  This software is copyrighted by and is the property of Generalplus    *
 *  Inc. All rights are reserved by Generalplus Inc.                      *
 *  This software may only be used in accordance with the                 *
 *  corresponding license agreement. Any unauthorized use, duplication,   *
 *  distribution, or disclosure of this software is expressly forbidden.  *
 *                                                                        *
 *  This Copyright notice MUST not be removed or modified without prior   *
 *  written consent of Generalplus Technology Co., Ltd.                   *
 *                                                                        *
 *  Generalplus Inc. reserves the right to modify this software           *
 *  without notice.                                                       *
 *                                                                        *
 *  Generalplus Inc.                                                      *
 *  No.19, Industry E. Rd. IV, Hsinchu Science Park                       *
 *  Hsinchu City 30078, Taiwan, R.O.C.                                    *
 *                                                                        *
 **************************************************************************/
#include "drv_l1_sfr.h"
#include "drv_l1_mipi_dsi.h"

#if (defined _DRV_L1_MIPI_DSI) && (_DRV_L1_MIPI_DSI == 1)
typedef struct
{	// Offset
	volatile INT32U DSI_VM_HT_CTRL;			// 0x0000
	volatile INT32U DSI_VM_VT0_CTRL;		// 0x0004
	volatile INT32U DSI_VM_VT1_CTRL;		// 0x0008
	volatile INT32U DSI_VM_BLLP_CTRL;		// 0x000C
	volatile INT32U DSI_LPCK_CTRL;			// 0x0010
	volatile INT32U DSI_LANE_TIMING_CTRL;	// 0x0014
	volatile INT32U DSI_CL_TIMING0_CTRL;	// 0x0018
	volatile INT32U DSI_CL_TIMING1_CTRL;	// 0x001C
	volatile INT32U RESERVED_1;				// 0x0020
	volatile INT32U DSI_SPKT_HEADER_CTRL;	// 0x0024
	volatile INT32U DSI_LPKT_HEADER_CTRL;	// 0x0028
	volatile INT32U DSI_LPKT_PAYLOAD_CTRL;	// 0x002C
	volatile INT32U DSI_VM_INPUT_CTRL;		// 0x0030
	volatile INT32U DSI_BLANK_CTRL;			// 0x0034
	volatile INT32U DSI_OP_CTRL;			// 0x0038
	volatile INT32U DSI_CTRL;				// 0x003C
	volatile INT32U DSI_COMMAND_FIFO_STA;	// 0x0040
	volatile INT32U DSI_BTA_CTRL;			// 0x0044
	volatile INT32U DSI_LANE_SWAP;			// 0x0048
	volatile INT32U DSI_RGBWC_CTRL;			// 0x004C
	volatile INT32U DSI_RTD_CTRL;			// 0x0050
	volatile INT32U DSI_ACK_RSP_STA;		// 0x0054
	volatile INT32U DSI_RSP_DBC;			// 0x0058
	volatile INT32U RESERVED_2;				// 0x005C
	volatile INT32U DSI_INT_STA;			// 0x0060
	volatile INT32U DSI_INT_EN_CTRL;		// 0x0064
	volatile INT32U DSI_ERR_STA;			// 0x0068
	volatile INT32U RESERVED_3;				// 0x006C
	volatile INT32U DSI_STA;				// 0x0070
	volatile INT32U DSI_WUD_CTRL;			// 0x0074
	volatile INT32U DSI_CK_CTRL;			// 0x0078
	volatile INT32U RESERVED_4;				// 0x007C
	volatile INT32U DSI_DEBUG_STA;			// 0x0080
	volatile INT32U DSI_ANALOG_CTRL;		// 0x0084
} DSI_SFR;

/*****************************************************
* get_DSI_Reg_Base:
*
*****************************************************/
static DSI_SFR *get_DSI_Reg_Base(void)
{
	return (DSI_SFR *) DSI_BASE;
}

void drv_l1_mipi_dsi_init(void)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	pDSI_Reg->DSI_CTRL = 0;
}

// Writing "1" to this bit will reset the entire module except local register setting. This bit will be cleared after the reset is completed.

// The MIPI link will enter TX stop state immediately and any outgoing MIPI packet will be terminated immediately.
void drv_l1_mipi_dsi_reset(void)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	pDSI_Reg->DSI_OP_CTRL = 0x1;
}

void drv_l1_mipi_dsi_uninit(void)
{
	drv_l1_mipi_dsi_reset();
	drv_l1_mipi_dsi_init();
}

// set BAT Mode Timing.

// B[8] : Back2LP11 (Don't Care).For BTA, Master receive unsupported state, the FSM will enter ERR State and can back to init state by Back2LP11 bit.
void drv_l1_mipi_dsi_lpck_set(INT32U divide)
{
	INT32U	value;
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	value = pDSI_Reg->DSI_LPCK_CTRL;
	value &= ~MASK_5_BIT;
	value |= (divide & MASK_5_BIT);
	pDSI_Reg->DSI_LPCK_CTRL = value;
}

// get ACK Response Error Status status.
INT32U drv_l1_mipi_dsi_ack_error_response_status_get(void)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	return pDSI_Reg->DSI_ACK_RSP_STA;
}

// get int status.
INT32S drv_l1_mipi_dsi_int_status_get(INT32U mask, INT32U time_out)
{
	INT32U	i, enable, status, value = 0;
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	for(i = 0; i < time_out; i++)
	{
		enable = mask;
		status = pDSI_Reg->DSI_INT_STA;
		enable &= status;

		if(enable & DSI_STATEUS_ACK_TRIG)
		{
			pDSI_Reg->DSI_INT_STA = DSI_STATEUS_ACK_TRIG;
			value |= DSI_STATEUS_ACK_TRIG;
		}

		if(enable & DSI_STATUS_RX_DAFULL)
		{
			pDSI_Reg->DSI_INT_STA = DSI_STATUS_RX_DAFULL;
			value |= DSI_STATUS_RX_DAFULL;
		}

		if(enable & DSI_STATUS_RX_DFULL)
		{
			pDSI_Reg->DSI_INT_STA = DSI_STATUS_RX_DFULL;
			value |= DSI_STATUS_RX_DFULL;
		}

		if(enable == value && status)
			break;
	}

	return value;
}

// set BAT Mode Timing.

// B[8] : Back2LP11 (Don't Care).For BTA, Master receive unsupported state, the FSM will enter ERR State and can back to init state by Back2LP11 bit.
void drv_l1_mipi_dsi_bta_timing_set(INT32U ta_get, INT32U ta_sure, INT32U ta_go)
{
	INT32U	value = 0;
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	value = (((ta_get & MASK_3_BIT) << SHIFT_20_BIT) | ((ta_get & MASK_3_BIT) << SHIFT_16_BIT) | (ta_go & MASK_3_BIT));
	pDSI_Reg->DSI_BTA_CTRL = value;
}

// This bit should be set "1" when intend to read Return Data (0x50).
// This bit is to prevent un-expected read cycle when IDE Memory window is enabled.	Remind turn off Memory Window first.

// Note: This bit is avoid memory window read value frist for IDE.Must be turn off Memory Window first, when RSP_RDEN enable.
void drv_l1_mipi_dsi_memory_window_read_enable(INT32U enable)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(enable)
		pDSI_Reg->DSI_OP_CTRL |= DSI_OP_RSP_RDEN;
	else
		pDSI_Reg->DSI_OP_CTRL &= ~DSI_OP_RSP_RDEN;
}

// Enable Low-Power Mode for transmission.
// 0x0 : Disable.

// 0x1 : Enable.
void drv_l1_mipi_dsi_tx_lpdt_enable(INT32U enable)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(enable)
		pDSI_Reg->DSI_OP_CTRL |= DSI_OP_TXLPDT;
	else
		pDSI_Reg->DSI_OP_CTRL &= ~DSI_OP_TXLPDT;
}

// Enable the automatic bus turn-around after completion of each short packet transmission.
// 0x0 : Disable.
// 0x1 : Enable.

// BTA auto run on Read command mode.
void drv_l1_mipi_dsi_long_BTA_enable(INT32U enable)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(enable)
		pDSI_Reg->DSI_OP_CTRL |= DSI_OP_LONG_BTA_EN;
	else
		pDSI_Reg->DSI_OP_CTRL &= ~DSI_OP_LONG_BTA_EN;
}

// Enable the automatic bus turn-around after completion of each short packet transmission.
// 0x0 : Disable.
// 0x1 : Enable.

// BTA auto run on Read command mode.
void drv_l1_mipi_dsi_short_BTA_enable(INT32U enable)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(enable)
		pDSI_Reg->DSI_OP_CTRL |= DSI_OP_SHORT_BTA_EN;
	else
		pDSI_Reg->DSI_OP_CTRL &= ~DSI_OP_SHORT_BTA_EN;
}

// Enable Analog PHY.
// 0x0 : Disable.
// 0x1 : Enable.
// Enable PHY Circuit have two condition:
// 1.B[24] must be 0x1.

// 2.Clock Lane is not on ULPS mode (Must be set bit19 of 0x38, will entry Clock ULPS mode).Default,clock lane not on ULPS mode, so must control this bit.
void drv_l1_mipi_dsi_analog_phy_enable(INT32U enable)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(enable)
	{
		pDSI_Reg->DSI_CTRL |= DSI_ANALOG_PHY_EN;
		while(!(pDSI_Reg->DSI_STA & DSI_ANALOG_PHY_EN));
	}
	else
		pDSI_Reg->DSI_CTRL &= ~DSI_ANALOG_PHY_EN;
}

// Enable EoTp Packet.
// 0x0 : Do not send EoTp packet.
// 0x1 : Send EoTp packet .

// This bit specifies whether the Bridge will send out the EOT packet at the end of HS transmission or not.
void drv_l1_mipi_dsi_eotp_packet_enable(INT32U enable)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(enable)
		pDSI_Reg->DSI_CTRL |= B_DISP_EOTP_PACKET_EN;
	else
		pDSI_Reg->DSI_CTRL &= ~B_DISP_EOTP_PACKET_EN;
}

// Data Lane Set.
// 0x0 : 1 lane.
// 0x1 : 2 lane.
// 0x2 : Reserved

// 0x3 : 4 lane.
void drv_l1_mipi_dsi_data_lane_set(INT32U number)
{
#define LANE_MODE	1
	DSI_SFR *pDSI_Reg;
	pDSI_Reg = get_DSI_Reg_Base();

	pDSI_Reg->DSI_CTRL &= ~DSI_DATA_LANE_MASK;

#if LANE_MODE == 1
	pDSI_Reg->DSI_CTRL |= (((number - 1) << B_DISP_DATA_LANE) & DSI_DATA_LANE_MASK);
#else
	pDSI_Reg->DSI_CTRL |= ((number << B_DISP_DATA_LANE) & DSI_DATA_LANE_MASK);
#endif
}

// Enable MIPI DSI Command TX type.
// 0x0 : LP Escape Mode.
// 0x1 : HSA Mode
// NOTE: HW Design:
// 1.Video Mode start and Blanking period use HSM mode for command.

// 2.In real case usually command mode stop befor Video Mode start.It is not Blanking Period use Command.
void drv_l1_mipi_dsi_command_tx_type_set(INT32U type)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(type)
		pDSI_Reg->DSI_CTRL |= DSI_ENGINE_COMMAND_TXLP_EN;
	else
		pDSI_Reg->DSI_CTRL &= ~DSI_ENGINE_COMMAND_TXLP_EN;
}

// Enable MIPI DSI Command Mode.
// 0x0 : RGB data comes from RGB Interface (TFT Control) and the output of  DSI_ENGINE is operating at Video Mode
// 0x1 : Command Mode RGB Interface (for frame buffer Panel used)
//       RGB data comes from MCU interface (Command mode) and they are transmitted at HSM.
//       Also, other commands can be transmitted at LPM or HSM depending on bit-2.

// NOTE: Mode1 for Command use LPM. Mode0 for Command use LPM or HSM on blanking.
void drv_l1_mipi_dsi_command_mode_enable(INT32U enable)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(enable)
		pDSI_Reg->DSI_CTRL |= DSI_ENGINE_COMMAND_MODE_EN;
	else
		pDSI_Reg->DSI_CTRL &= ~DSI_ENGINE_COMMAND_MODE_EN;
}

// Enable MIPI DSI Engine.
// 0x0 : Disable.

// 0x1 : Enable.
void drv_l1_mipi_dsi_enable(INT32U enable)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	if(enable)
		pDSI_Reg->DSI_CTRL |= DSI_ENGINE_EN;
	else
		pDSI_Reg->DSI_CTRL &= ~DSI_ENGINE_EN;
}

// This register is not used for video mode data (Only for Command mode)

// because the short packets are generated by HW base on the transmission packet sequence specified in DSI spec.
void drv_l1_mipi_dsi_short_packet_header_set(INT32U data_type, INT32U data0, INT32U data1)
{
	//Note : ECC is produce by HW, so B[31:24] = 0x0.
	INT32U	value = 0;
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	value = (((data1 & MASK_8_BIT) << SHIFT_16_BIT) | ((data0 & MASK_8_BIT) << SHIFT_8_BIT) | (data_type & MASK_8_BIT));
	pDSI_Reg->DSI_SPKT_HEADER_CTRL = value;
}

// The register is not used in video mode because payload data are provided by video port.
static void drv_l1_mipi_dsi_long_packet_payload_set(INT8U data0, INT8U data1, INT8U data2, INT8U data3)
{
	INT32U	value = 0;
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	value = ((data3 << SHIFT_24_BIT) | (data2 << SHIFT_16_BIT) | (data1 << SHIFT_8_BIT) | data0);
	pDSI_Reg->DSI_LPKT_PAYLOAD_CTRL = value;
}

// This register is used for command mode.
void drv_l1_mipi_dsi_long_packet_header_set(INT32U data_type, INT32U byte_cnt, INT8U *data_ptr)
{
	INT8U	temp[4], *temp_ptr;
	INT32U	i, remainder, word_cnt, value = 0;
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	value = (((byte_cnt & MASK_16_BIT) << SHIFT_8_BIT) | (data_type & MASK_8_BIT));
	pDSI_Reg->DSI_LPKT_HEADER_CTRL = value;

	temp_ptr = data_ptr;

	if(byte_cnt <= 4)
	{
		temp[0] = *temp_ptr;
		temp_ptr++;
		temp[1] = *temp_ptr;
		temp_ptr++;
		temp[2] = *temp_ptr;
		temp_ptr++;
		temp[3] = *temp_ptr;
		temp_ptr++;
		drv_l1_mipi_dsi_long_packet_payload_set(temp[0], temp[1], temp[2], temp[3]);
	}
	else
	{
		temp[0] = (byte_cnt % 4);
		if(temp[0])
		{
			word_cnt = ((byte_cnt / 4) + 1);
			remainder = temp[0];
		}
		else
		{
			word_cnt = (byte_cnt / 4);
			remainder = 0;
		}

		for(i = 0; i < word_cnt; i++)
		{
			if(remainder)
			{
				if(i == (word_cnt - 1))
				{
					switch(remainder)
					{
					case 1:
						temp[0] = *temp_ptr;
						drv_l1_mipi_dsi_long_packet_payload_set(temp[0], 0, 0, 0);
						break;

					case 2:
						temp[0] = *temp_ptr;
						temp_ptr++;
						temp[1] = *temp_ptr;
						drv_l1_mipi_dsi_long_packet_payload_set(temp[0], temp[1], 0, 0);
						break;

					case 3:
						temp[0] = *temp_ptr;
						temp_ptr++;
						temp[1] = *temp_ptr;
						temp_ptr++;
						temp[2] = *temp_ptr;
						drv_l1_mipi_dsi_long_packet_payload_set(temp[0], temp[1], temp[2], 0);
						break;
					}
				}
				else
				{
					temp[0] = *temp_ptr;
					temp_ptr++;
					temp[1] = *temp_ptr;
					temp_ptr++;
					temp[2] = *temp_ptr;
					temp_ptr++;
					temp[3] = *temp_ptr;
					temp_ptr++;
					drv_l1_mipi_dsi_long_packet_payload_set(temp[0], temp[1], temp[2], temp[3]);
				}
			}
			else
			{
				temp[0] = *temp_ptr;
				temp_ptr++;
				temp[1] = *temp_ptr;
				temp_ptr++;
				temp[2] = *temp_ptr;
				temp_ptr++;
				temp[3] = *temp_ptr;
				temp_ptr++;
				drv_l1_mipi_dsi_long_packet_payload_set(temp[0], temp[1], temp[2], temp[3]);
			}
		}
	}
}

// B[29] : LP-RX Data FIFO FULL Status.Inform processor must read the data in FIFO; otherwise, there will be data to be lost!!
// B[28] : LP-RX Data Almost FULL Status (Half).Indicates there are half amount of FIFO can be read.

// B[24] : ACK_TRIG. This bit reflects whether the ACK trigger message has received or not.
INT32U drv_l1_mipi_dsi_interrupt_state_get(void)
{
	DSI_SFR *pDSI_Reg;

	pDSI_Reg = get_DSI_Reg_Base();

	return pDSI_Reg->DSI_INT_STA;
}

#endif //(defined _DRV_L1_MIPI_DSI) && (_DRV_L1_MIPI_DSI == 1)
